home *** CD-ROM | disk | FTP | other *** search
/ User's Choice Windows CD / User's Choice Windows CD (CMS Software)(1993).iso / windows4 / qbnws302.zip / HASH.ZIP / HASH.BAS
BASIC Source File  |  1992-06-20  |  16KB  |  518 lines

  1. ' Hashed Access Demonstration Program For The QuickBasic Echo
  2. ' By Mike Avery, Started 12-28-91
  3. ' Version 1:00.00 - Make it work. 12-28-91
  4. ' Version 1:01.00 - Add Disk Functions 12-29-91
  5. ' ========================================================================
  6.  
  7. DECLARE FUNCTION Hash! (TestString$)
  8. DECLARE SUB GetData (Key$, Index%, SeekCount%, SaveIndex%)
  9. DECLARE SUB Waiter ()
  10. DECLARE FUNCTION WhackIt$ (InputString$)
  11.  
  12. CONST DeletedValue$ = "EXPLETIVE DELETED"
  13. CONST ArraySize% = 531         'Change the size here - the rest adjusts itself
  14. CONST RetryLimit% = 100        'I get bored easily....
  15. CONST ScreenLimit% = 21        'how many lines do we show at once?
  16. CONST True = -1: CONST False = NOT (True)
  17.  
  18. DIM SHARED A$(ArraySize%, 1)          'our little data base
  19. DIM SHARED SortSpace$(ArraySize%, 1)  'Workspace for sorted lists
  20.  
  21. PowerMax% = INT((LOG(ArraySize%) / LOG(2)) + 2)
  22. DIM SHARED PowersOfTwo%(PowerMax%)
  23.  
  24. 'build the table - lookup is faster than calculation
  25. FOR I% = 0 TO PowerMax%
  26.     PowersOfTwo%(I%) = 2 ^ I%
  27. NEXT I%
  28.  
  29. DO WHILE TestName$ <> "STOP"
  30.    CLS
  31.    PRINT "Doofus Phone Book System"
  32.    PRINT
  33.    PRINT
  34.    INPUT "Name/Help/Dump/Sort/Load/Save/Analyse/Stop"; TestName$
  35.    TestName$ = UCASE$(RTRIM$(LTRIM$(TestName$)))
  36.   
  37.    IF TestName$ = "DUMP" THEN
  38.       GOSUB DumpIt
  39.  
  40.    ELSEIF TestName$ = "SORT" THEN
  41.       GOSUB SortIt
  42.  
  43.    ELSEIF TestName$ = "ANALYSE" THEN
  44.       GOSUB Analyse
  45.  
  46.    ELSEIF TestName$ = "HELP" THEN
  47.       GOSUB Help
  48.  
  49.    ELSEIF TestName$ = "LOAD" THEN
  50.       GOSUB LoadIt
  51.  
  52.    ELSEIF TestName$ = "SAVE" THEN
  53.       GOSUB SaveIt
  54.   
  55.    ELSEIF TestName$ <> "" AND TestName$ <> "STOP" THEN
  56.       CALL GetData(TestName$, Index%, SeekCount%, SaveIndex%)
  57.  
  58.       ' At this point, one of 3 conditions exists.
  59.       ' 1. We ran out of retries, and it doesn't matter what Index% points to,
  60.       ' 2. Index% points to our data, or
  61.       ' 3. Index% points to an empty record and SaveIndex may or may not
  62.       '    point to a deleted record we can reuse.
  63.      
  64.       PRINT
  65.       PRINT "It took "; SeekCount%; "tries to determine that..."
  66.       'in a productional program, you'd probably drop that message...
  67.  
  68.       PRINT
  69.  
  70.       IF SeekCount% >= RetryLimit% THEN
  71.          PRINT "The data base is full and/or needs to be resized"
  72.          YesOrNo$ = ""
  73.          DO WHILE YesOrNo$ <> "Y" AND YesOrNo$ <> "N"
  74.             INPUT "Do you want to see a data base dump (Y/N)"; YesOrNo$
  75.             IF YesOrNo$ <> "" THEN
  76.                YesOrNo$ = WhackIt$(YesOrNo$)
  77.             END IF
  78.             IF YesOrNo$ = "Y" THEN
  79.                GOSUB DumpIt
  80.             ELSEIF YesOrNo$ <> "N" THEN
  81.                PRINT "Please Enter A Y for Yes or a N for NO."
  82.             END IF
  83.          LOOP
  84.  
  85.          TestName$ = "STOP"'force a shutdown
  86.          CALL Waiter
  87.          ' save data base here, if converted to a disk based system
  88.  
  89.       ELSEIF A$(Index%, 0) = TestName$ THEN
  90.          PRINT A$(Index%, 0); "'s Phone Number Is "; A$(Index%, 1); "."
  91.          Action$ = "Dummy"
  92.          DO WHILE Action$ <> "" AND Action$ <> "C" AND Action$ <> "D"
  93.             INPUT "Change the number, Delete The Number, or enter"; Action$
  94.             
  95.             IF Action$ <> "" THEN
  96.                Action$ = WhackIt$(Action$)
  97.               
  98.                IF Action$ = "C" THEN
  99.                   'else if we are to change the number
  100.                   INPUT "New phone number please"; PhoneNumber$
  101.                   PhoneNumber$ = UCASE$(RTRIM$(LTRIM$(PhoneNumber$)))
  102.  
  103.                   IF PhoneNumber$ = "" THEN
  104.                      PRINT "Number not changed"
  105.                   ELSE
  106.                      A$(Index%, 1) = PhoneNumber$
  107.                      PRINT "Phone number has been updated."
  108.                   END IF
  109.  
  110.                ELSEIF Action$ = "D" THEN
  111.                   A$(Index%, 0) = DeletedValue$
  112.                   PRINT "Entry has been deleted."
  113.  
  114.                ELSE
  115.                   'an invalid entry was made
  116.                   PRINT "Please enter a D to Delete the number,"
  117.                   PRINT "a C to Change it, or"
  118.                   PRINT "just press Enter to continue."
  119.                   Action$ = "DUMMY"
  120.                END IF
  121.             END IF
  122.          LOOP
  123.  
  124.       ELSE
  125.          PRINT TestName$; "'s Phone Number Is Not On File.  You May Enter It To Add"
  126.          PRINT "It, Or Just Press "; CHR$(34); "ENTER"; CHR$(34); " To Continue.";
  127.          INPUT PhoneNumber$
  128.          PhoneNumber$ = UCASE$(RTRIM$(LTRIM$(PhoneNumber$)))
  129.  
  130.          IF PhoneNumber$ <> "" THEN
  131.             IF SaveIndex% <> -1 THEN
  132.                'reuse delete space
  133.                Index% = SaveIndex%
  134.                PRINT "We are reclaiming unused space!  Ain't it great!"
  135.                CALL Waiter
  136.             END IF
  137.  
  138.             A$(Index%, 0) = TestName$
  139.             A$(Index%, 1) = PhoneNumber$
  140.          END IF
  141.       END IF
  142.  
  143.    END IF
  144. LOOP
  145.  
  146. ExitRoutine:
  147. SYSTEM
  148.  
  149. Analyse:
  150. 'process all the data elements in A$ to see:
  151. ' how full A$ is,
  152. ' best and worst case access to A$,
  153. ' mean, SD of access count
  154.  
  155. ' Statistics routines "borrowed" in part from
  156. ' "Some Common Basic Programs" pg 121-122
  157. ' by Lon Poole and Mary Borchers
  158. ' Published by Adam Osborne
  159. ' Copyright 1977
  160. ' pages 121-123
  161. PRINT "Analysis Begins.... Please Wait....."
  162.  
  163. Best% = 999
  164. Worst% = 0
  165. S = 0 ' we are dealing with a population, not a sample
  166. N = 0 ' count of active elements
  167. M = 0 ' Sum of X^2
  168. P = 0 ' Sum of X
  169.  
  170. FOR I% = 0 TO ArraySize%
  171.     IF A$(I%, 0) <> "" AND A$(I%, 0) <> DeletedValue$ THEN
  172.        CALL GetData(A$(I%, 0), Index%, Tries%, FirstDeleted%)
  173.        N = N + 1              ' Bump entry count
  174.        P = P + Tries%         ' Bump sum of X
  175.        M = M + (Tries% ^ 2)   ' Bump sum of X^2
  176.  
  177.        IF Tries% < Best% THEN
  178.           Best% = Tries%
  179.           BestOne% = Index%
  180.        END IF
  181.  
  182.        IF Tries% > Worst% THEN
  183.           Worst% = Tries%
  184.           WorstOne% = Index%
  185.        END IF
  186.     END IF
  187. NEXT I%
  188.  
  189. IF N > 0 THEN
  190.    PRINT "Access Analysis....."
  191.    R = P / N
  192.    PRINT "Number Of Entries ="; N
  193.    PRINT "Percent Full ="; INT((N / (ArraySize% + 1)) * 100); "%"
  194.    PRINT "Average Access ="; R; "Seeks."
  195.    V = (M - N * R ^ 2) / (N - S)
  196.    SD = SQR(V)
  197.    PRINT "Standard Deviation ="; SD
  198.    PRINT "Best Access ="; Best%; "Seeks On "; A$(BestOne%, 0); "."
  199.    PRINT "Worst Access ="; Worst%; "Seeks On "; A$(WorstOne%, 0); "."
  200. ELSE
  201.    PRINT "No Data To Analyze.  Sorry."
  202. END IF
  203.  
  204. CALL Waiter
  205. RETURN
  206.  
  207. DumpIt:
  208. DisplayControl% = 0
  209. FOR I% = 0 TO ArraySize%
  210.     PRINT I%, A$(I%, 0), A$(I%, 1)
  211.     DisplayControl% = DisplayControl% + 1
  212.     IF DisplayControl% > ScreenLimit% THEN
  213.        CALL Waiter
  214.        DisplayControl% = 0
  215.     END IF
  216. NEXT I%
  217.  
  218. CALL Waiter
  219. RETURN
  220.  
  221. ErrorHandler:
  222.  
  223. PRINT "ErrorHandler Sez...."
  224.  
  225. IF ERR = 53 OR ERR = 76 OR ERR = 68 OR ERR = 52 OR ERR = 64 OR ERR = 75 THEN
  226.    PRINT "A file you wanted to process, "; FileName$
  227.    PRINT "Could not be found/created."
  228.    Found = False
  229.    CALL Waiter
  230.    RESUME NEXT
  231. END IF
  232.  
  233. IF ERR = 61 THEN
  234.    PRINT "Sorry, the disk is full."
  235. ELSE
  236.    PRINT "You had an Error #"; ERR
  237. END IF
  238.  
  239. PRINT "Press any key to quit...."
  240. K$ = ""
  241. DO WHILE K$ = ""
  242.    K$ = INKEY$
  243. LOOP
  244. RESUME ExitRoutine
  245.  
  246. Help:
  247. 'Display a primitive help screen
  248. CLS
  249. PRINT "Doofus Phone Book System"
  250. PRINT
  251. PRINT
  252. PRINT "Name/Help/Dump/Sort/Load/Save/Analyse/Stop? HELP"
  253. PRINT
  254. PRINT "The Doofus Phone Book System was written as a demonstration of Hashed"
  255. PRINT "Data Access, rather than as a phone book system.  If it works for you,"
  256. PRINT "fine, but that was not the author's intent."
  257. PRINT
  258. PRINT "At the first prompt "; CHR$(34); "Name/Help/Dump/Sort/Load/Save/Analyse:"; CHR$(34); ","
  259. PRINT "You may enter a name to be added or looked up in the data base by entering"
  260. PRINT "the name."
  261. PRINT "You may ask for help by entering "; CHR$(34); "HELP"; CHR$(34); "."
  262. PRINT "You may see a raw dump of the data array by entering "; CHR$(34); "DUMP"; CHR$(34); "."
  263. PRINT "You may see a sorted data dump of the array by entering "; CHR$(34); "SORT"; CHR$(34); "."
  264. PRINT "You may load or save the data to/from disk with the LOAD and SAVE commands."
  265. PRINT "You may analyse the data set by entering the command "; CHR$(34); "ANALYSE"; CHR$(34); "."
  266. PRINT "You may exit the application by entering the command "; CHR$(34); "STOP"; CHR$(34); "."
  267. CALL Waiter
  268. CLS
  269. PRINT "Doofus Phone Book System"
  270. PRINT
  271. PRINT
  272. PRINT "Name/Help/Dump/Sort/Load/Save/Analyse/Stop? HELP"
  273. PRINT
  274. PRINT "Once you have called up a phone number entry, you may continue by pressing"
  275. PRINT CHR$(34); "ENTER"; CHR$(34); ", or you may change the data by entering a "; CHR$(34); "C"; CHR$(34); ","
  276. PRINT "or you may delete the data by pressing a "; CHR$(34); "D"; CHR$(34); "."
  277. CALL Waiter
  278. CLS
  279. RETURN
  280.  
  281. LoadIt:
  282. 'load the data from a data file
  283.  
  284. Free% = 0
  285. Empty% = Empty% + 1
  286.  
  287. FOR I% = 0 TO ArraySize%
  288.     IF A$(I%, 0) = "" THEN
  289.        Free% = Free% + 1
  290.        Empty% = Empty% + 1
  291.    
  292.     ELSEIF A$(I%, 0) = DeletedValue$ THEN
  293.        Free% = Free% + 1
  294.     END IF
  295. NEXT I%
  296.  
  297. IF Empty% = 0 THEN
  298.    GOSUB SorryFull
  299.  
  300. ELSE
  301.    INPUT "File To Load From:"; FileName$
  302.    ON ERROR GOTO ErrorHandler
  303.    Found = True
  304.    OPEN FileName$ FOR INPUT AS 1
  305.  
  306.    IF Found = True THEN
  307.       DO WHILE NOT EOF(1) AND Free% > 0
  308.          INPUT #1, TestName$, PhoneNumber$
  309.          CALL GetData(TestName$, Index%, Seeks%, SaveIndex%)
  310.  
  311.          IF SeekCount% >= RetryLimit% THEN
  312.             PRINT "The data base is full and/or needs to be resized"
  313.             YesOrNo$ = ""
  314.             DO WHILE YesOrNo$ <> "Y" AND YesOrNo$ <> "N"
  315.                INPUT "Do you want to see a data base dump (Y/N)"; YesOrNo$
  316.                IF YesOrNo$ <> "" THEN
  317.                   YesOrNo$ = WhackIt$(YesOrNo$)
  318.                END IF
  319.                IF YesOrNo$ = "Y" THEN
  320.                   GOSUB DumpIt
  321.                ELSEIF YesOrNo$ <> "N" THEN
  322.                   PRINT "Please Enter A Y for Yes or a N for NO."
  323.                END IF
  324.             LOOP
  325.  
  326.             Free% = 0 'force a shutdown
  327.             CALL Waiter
  328.  
  329.          ELSEIF A$(Index%, 0) = TestName$ THEN
  330.             ' the value is already on file
  331.             ' we'll just replace the old value for now,
  332.             ' and keep on truckin - we could ask the user
  333.             ' what we should do, but not for a test program!
  334.             A$(Index%, 1) = PhoneNumber$
  335.             PRINT A$(Index%, 0); "has been updated!"
  336.  
  337.          ELSE
  338.             IF SaveIndex% <> -1 THEN
  339.                'reuse deleted space
  340.                Index% = SaveIndex%
  341.                PRINT "We are reclaiming unused space!  Ain't it great!"
  342.             END IF
  343.             A$(Index%, 0) = TestName$
  344.             A$(Index%, 1) = PhoneNumber$
  345.             Free% = Free% - 1
  346.          END IF
  347.  
  348.          IF Free% < 1 THEN
  349.             PRINT "The data base has been completely filled."
  350.             PRINT "Some data was not loaded from the file you selected."
  351.             PRINT
  352.             GOSUB SorryFull
  353.  
  354.             CALL Waiter
  355.          END IF
  356.       LOOP
  357.       CLOSE 1
  358.    END IF
  359.    ON ERROR GOTO 0
  360. END IF
  361.  
  362. RETURN
  363.  
  364. SaveIt:
  365. 'Save data to a selected file
  366.  
  367. ON ERROR GOTO ErrorHandler
  368.  
  369. INPUT "Name of file to save data to:"; FileName$
  370.  
  371. OPEN FileName$ FOR OUTPUT AS 1
  372. FOR I% = 0 TO ArraySize%
  373.     IF A$(I%, 0) <> "" AND A$(I%, 0) <> DeletedValue THEN
  374.        PRINT #1, A$(I%, 0); ","; A$(I%, 1)
  375.     END IF
  376. NEXT I%
  377. CLOSE 1
  378. RETURN
  379.  
  380. SortIt:
  381. ' convert, sort, and dump the data base
  382.  
  383. 'convert the hashed A$() into a packed SortSpace$()
  384.  
  385. PRINT "Converting the data into a linear array...."
  386. NextEntry% = 0
  387. FOR I% = 0 TO ArraySize%
  388.     IF A$(I%, 0) <> "" AND A$(I%, 0) <> DeletedValue$ THEN
  389.        SortSpace$(NextEntry%, 0) = A$(I%, 0)
  390.        SortSpace$(NextEntry%, 1) = STR$(I%)
  391.        'track the location of the data, not the data....
  392.        NextEntry% = NextEntry% + 1
  393.     END IF
  394. NEXT I%
  395.  
  396. IF NextEntry% <= 0 THEN
  397.    PRINT "No Data Was Found To Display."
  398.   
  399. ELSE
  400. 'now that all the data has been moved from A$() to SortSpace$(), we
  401. 'need to sort it.  How about an exchange sort?
  402.    LastItem% = NextEntry% - 1
  403.    IF LastItem% > 1 THEN
  404.       PRINT "Sorting"; LastItem% + 1; "items.  Please Wait....."
  405.       FOR I% = 0 TO LastItem% - 1
  406.           Lowest% = I%
  407.           FOR J% = I% + 1 TO LastItem%
  408.               CompareCount! = CompareCount! + 1
  409.               IF SortSpace$(J%, 0) < SortSpace$(Lowest%, 0) THEN
  410.                  Lowest% = J%
  411.               END IF
  412.           NEXT J%
  413.           IF Lowest% <> I% THEN
  414.              SWAP SortSpace$(I%, 0), SortSpace$(Lowest%, 0)
  415.              SWAP SortSpace$(I%, 1), SortSpace$(Lowest%, 1)
  416.           END IF
  417.       NEXT I%
  418.    ELSE
  419.       PRINT "1 item found, the sort will be skipped this time...."
  420.    END IF
  421.  
  422.    'Now the keys are sorted, so let's display the data....
  423.    PRINT "Order", "Name", "Phone #", "Place in A$"
  424.    
  425.    DisplayCount% = 0
  426.    FOR I% = 0 TO LastItem%
  427.        Pointer% = VAL(SortSpace$(I%, 1))
  428.        PRINT I%, A$(Pointer%, 0), A$(Pointer%, 1), Pointer%
  429.        DisplayCount% = DisplayCount% + 1
  430.        IF DisplayCount% > ScreenLimit% THEN
  431.           CALL Waiter
  432.           DisplayCount% = 0
  433.        END IF
  434.    NEXT I%
  435. END IF
  436. CALL Waiter
  437. RETURN
  438.  
  439. SorryFull:
  440. PRINT "Sorry, but there is no space available in the array."
  441. PRINT "Try saving your data, stopping this program, resizing"; CHR$(34); "ArraySize%"; CHR$(34); ","
  442. PRINT "reloading the saved data, and then retry this load."
  443. RETURN
  444.  
  445. SUB GetData (Key$, Index%, SeekCount%, SaveIndex%)
  446. ' Try to get the index to the record that that contains Key$ as it's key
  447. ' Key$ - the value are looking for
  448. ' Found% - did we find Key$ - True/False returned
  449. ' Index% - a pointer to where Key$ was found
  450. ' SeekCount% - how many tries it took us to fing Key$
  451. ' SaveIndex% - the pointer to the first deleted value we found, if any
  452.  
  453. Index% = Hash(Key$) 'start the search
  454. SaveIndex% = -1
  455. SeekCount% = 1
  456.  
  457. IF A$(Index%, 0) <> "" AND A$(Index%, 0) <> Key$ THEN
  458.    'if data in entry, and not a match, do a retry
  459.    ReHashCount% = 0
  460.  
  461.    DO WHILE SeekCount% < RetryLimit% AND A$(Index%, 0) <> "" AND A$(Index%, 0) <> Key$
  462.       IF A$(Index%, 0) = DeletedValue$ AND SaveIndex% = -1 THEN
  463.         'if this is the first deleted value, save it for data insertion
  464.         SaveIndex% = Index%
  465.       END IF
  466.  
  467.       Index% = Index% + PowersOfTwo%(ReHashCount%)
  468.       DO WHILE Index% > ArraySize%
  469.          Index% = Index% - ArraySize%
  470.       LOOP
  471.             
  472.       ReHashCount% = ReHashCount% + 1
  473.       IF ReHashCount% > PowerMax% THEN
  474.          ReHashCount% = 0
  475.       END IF
  476.  
  477.       SeekCount% = SeekCount% + 1
  478.             
  479.    LOOP
  480. END IF
  481.  
  482. END SUB
  483.  
  484. FUNCTION Hash (TestString$)
  485. ' turn TestString into a number in the range of 0 - ArraySize%
  486. ' the function can be tailored to suit the users needs
  487.  
  488. Trial = 0
  489.  
  490. FOR I% = 1 TO LEN(TestString$)
  491.     Trial = Trial + ASC(MID$(TestString$, I%, 1))
  492. NEXT I%
  493.  
  494. Hash = (Trial * Trial) MOD ArraySize%
  495.  
  496. END FUNCTION
  497.  
  498. SUB Waiter
  499. ' wait for a keypress, then return to caller
  500. PRINT "Press (almost) any key to continue..."
  501. K$ = ""
  502. DO WHILE K$ = ""
  503.    K$ = INKEY$
  504. LOOP
  505. END SUB
  506.  
  507. FUNCTION WhackIt$ (InputString$)
  508. 'whack the input string -
  509. ' strip leading and trailing spaces,
  510. ' make the remainder upper case, and
  511. ' make it a single letter response.
  512. TestString$ = UCASE$(RTRIM$(LTRIM$(InputString$)))
  513. IF LEN(TestString$) > 1 THEN
  514.    TestString$ = LEFT$(TestString$, 1)
  515. END IF
  516. WhackIt$ = TestString$
  517. END FUNCTION
  518.